/*
 * CallServlet.java
 *
 * Created on November 16, 2006, 10:38 AM
 */
package clicktodial.sip;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.sip.Address;
import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.SipFactory;
import javax.servlet.sip.SipServlet;
import javax.servlet.sip.SipServletMessage;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.SipServletResponse;
import javax.servlet.sip.SipSession;
import javax.servlet.sip.SipSessionEvent;
import javax.servlet.sip.SipSessionListener;
import java.util.Date;
import java.util.StringTokenizer;


/**
 * @author jp
 */
public class CallSipServlet extends SipServlet implements SipSessionListener {

    private static final Logger logger = 
            Logger.getLogger(CallSipServlet.class.getName());
    
    // inject the SipFactory
    @Resource(mappedName="sip/ClickToDial")
    private SipFactory sf;
    
    /**
     * Handle a request to end the call
     * @param request the request to end the call
     */
    protected void doBye(SipServletRequest request)
        throws ServletException, IOException 
    {
        logger.info("Got bye");
        
        SipSession session = request.getSession();

        long tiempoInicio = session.getCreationTime();
        //Cálculo de la duración de la llamada
        Date date = new Date();
        //long tiempoFin = session.getLastAccessedTime();
        long tiempoFin = date.getTime();
        int duracion = (int) ((tiempoFin - tiempoInicio)/1000);
        int hora = date.getHours();
        logger.info("Duracion de la llamada " + duracion + " llamada realizada a las " + hora);

        //Puerto de conexión al servidor
        int port = 4444;

        //Localización del servidor.
        String host = "localhost";

        //DatagramSocket que escucha en cualquier puerto.
        DatagramSocket sock = new DatagramSocket();
        logger.info("Socket del cliente creado con éxito.");

        //Array que se va a usar para recibir información
        byte[] data = new byte[1500];

        String origen = request.getSession().getAttribute("origen").toString();
        String destino = request.getSession().getAttribute("destino").toString();
        //Separamos cada campo enviado con puntos
        String datosEnviados = hora + "." + destino + "." + origen + "." + duracion + ".";
        //Información enviada al servidor
        byte[] info = datosEnviados.getBytes();

        //InetAddress del servidor.
        InetAddress address = InetAddress.getByName(host);

        DatagramPacket peticion = new DatagramPacket(info,info.length,address,port);

        sock.send(peticion);
        logger.info("Petición enviada al servidor");

        // end the linked call 
        SipSession linkedSession = (SipSession) session.getAttribute("LinkedSession");
        if (linkedSession != null) {
            // create a BYE request to the linked session
            SipServletRequest bye = linkedSession.createRequest("BYE");
            
            logger.info("Sending bye to " + linkedSession.getRemoteParty());
            
            // send the BYE request
            bye.send();
        }
        
        // send an OK for the BYE
        SipServletResponse ok = request.createResponse(SipServletResponse.SC_OK);
        ok.send();

        //Esperamos la respuesta del servidor de reglas
        DatagramPacket recibido = new DatagramPacket(data, data.length);
        sock.receive(recibido);

        //Recibimos la factura
        StringTokenizer respuesta = new StringTokenizer(new String(recibido.getData()), ".");
        String operador = respuesta.nextToken();
        String segundos = respuesta.nextToken();
  	    //double precio = (double) (Integer.parseInt(respuesta.nextToken())/100);
        int precio = Integer.parseInt(respuesta.nextToken());
        request.getSession().setAttribute("operador", operador);
        request.getSession().setAttribute("segundos", segundos);
        request.getSession().setAttribute("precio", precio);
        request.getSession().setAttribute("fin", "fin");
        logger.info("Llamada a " + operador + " de duracion " + duracion + ": Precio - " + precio);



    }
    
    /**
     * Handle a request to cancel a call in progress
     * @param request the request to end the call
     */
    protected void doCancel(SipServletRequest request) 
        throws ServletException, IOException 
    {
        logger.info("Got cancel");
    }
    
    
    /**
     * Handle a success response.  When a call sends an OK message, we 
     * complete the call at the bridge.
     * @param resp the success response
     */  
    protected void doSuccessResponse(SipServletResponse resp)
        throws ServletException, IOException 
    {
        logger.info("Got OK");
        
        SipSession session = resp.getSession();
                
        // if the response was OK, invite the other party
        if (resp.getStatus() == SipServletResponse.SC_OK) {
            
            // first check if this is a duplicate OK
            Boolean inviteSent = (Boolean) session.getAttribute("InviteSent");
            if (inviteSent != null && inviteSent.booleanValue()) {
                return;
            }
            
            // we are in one of two states here.  Either this OK is from
            // the original invitee, meaning they answered and it is time
            // to ring the second party, or the OK is from the second
            // party, and we should acknowlege the call with both sides
            
            
            // check for the first case, where this OK is from the original
            // invitee
            Address secondPartyAddress = 
                    (Address) resp.getSession().getAttribute("SecondPartyAddress");  
            if (secondPartyAddress != null) {
                
                // invite the second party
                SipServletRequest invite = sf.createRequest(resp.getApplicationSession(),
                        "INVITE", session.getRemoteParty(), secondPartyAddress);
                
                logger.info("Found second party -- sending INVITE to " + secondPartyAddress);
                
                // get the SDP from the OK message
                if (resp.getContentType().equals("application/sdp")) {
                    invite.setContent(resp.getContent(), "application/sdp");
                }        
                
                // link the sessions
                session.setAttribute("LinkedSession", invite.getSession());
                invite.getSession().setAttribute("LinkedSession", session);
                
                // store the ack for the first party -- we'll send it when
                // we get information from the second party
                SipServletRequest ack = resp.createAck();
                invite.getSession().setAttribute("FirstPartyAck", ack);
                
                // send the invite
                invite.send();
                
                // prevent duplicates
                session.setAttribute("InviteSent", Boolean.TRUE);
            } else {
                logger.info("Got OK from second party -- sending ACK");
                
                // send acks
                SipServletRequest secondPartyAck = resp.createAck();
                SipServletRequest firstPartyAck = 
                        (SipServletRequest) resp.getSession().getAttribute("FirstPartyAck");
            
                // get the SDP from the second party's OK message
                if (resp.getContentType().equals("application/sdp")) {
                    firstPartyAck.setContent(resp.getContent(), "application/sdp");
                    //secondPartyAck.setContent(resp.getContent(), "application/sdp");
                }
                
                firstPartyAck.send();
                secondPartyAck.send();
            }
        }
    }
    
    /**
     * Handle an error response.  Close the relevant call at the bridge.
     * @param resp the error response
     */
    protected void doErrorResponse(SipServletResponse resp) 
        throws ServletException, IOException 
    {
        logger.info("Got error");
        
        // cancel the call
        resp.getSession().invalidate();
    }
   
    ////////
    // SipSessionListener interface
    
    /**
     * Called when a session is created.  Ignore.
     * @param sse the event
     */
    public void sessionCreated(SipSessionEvent sse) {
        logger.info("Session created");
    }

    /**
     * Called when a session is removed.  Remove any call data associated
     * with the given session.
     * @param sse the event
     */
    public void sessionDestroyed(SipSessionEvent sse) {
        logger.info("Session destroyed");
    }
}
